@inherits ComponentBase
@inject GlobalNotificationService GlobalNotificationService
@inject ConfirmModalService ConfirmModalService
@inject IStringLocalizerFactory LocalizerFactory
@using AliasVault.RazorComponents.Services
@using TotpGenerator
@using Microsoft.Extensions.Localization

<div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
    <div class="flex justify-between">
        <div>
            <h3 class="mb-4 text-xl font-semibold dark:text-white">@Localizer["TwoFactorAuthenticationTitle"]</h3>
        </div>
        @if (TotpCodeList.Any(t => !t.IsDeleted) && !IsAddFormVisible)
        {
            <div>
                <button id="add-totp-code" @onclick="ShowAddForm" type="button" class="text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-2 text-center dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-600 dark:focus:ring-blue-800">
                    @Localizer["AddTotpCodeButton"]
                </button>
            </div>
        }
    </div>

    @if ((TotpCodeList.Count == 0 || TotpCodeList.All(t => t.IsDeleted)) && !IsAddFormVisible)
    {
        <div class="flex flex-col justify-center">
            <p class="text-gray-500 dark:text-gray-400"><a @onclick="ShowAddForm" id="add-totp-code" href="javascript:void(0)" class="text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-400">@Localizer["AddTotpCodeDescription"]</a></p>
        </div>
    }
    else
    {
        @if (IsAddFormVisible)
        {
            <div class="p-4 mb-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600">
                <EditForm Model="@NewTotpCode" OnValidSubmit="AddTotpCode">
                    <DataAnnotationsValidator />
                    <div class="flex justify-between items-center mb-4">
                        <h4 class="text-lg font-medium text-gray-900 dark:text-white">@Localizer["AddTotpCodeModalTitle"]</h4>
                        <button @onclick="HideAddForm" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white">
                            <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
                                <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
                            </svg>
                            <span class="sr-only">@Localizer["CloseFormButton"]</span>
                        </button>
                    </div>
                    <p class="mb-4 text-sm text-gray-500 dark:text-gray-400">@Localizer["TotpInstructions"]</p>
                    <div class="mb-4">
                        <label for="name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["NameOptionalLabel"]</label>
                        <InputText id="name" @bind-Value="NewTotpCode.Name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" />
                        <ValidationMessage For="@(() => NewTotpCode.Name)" class="text-red-600 dark:text-red-400 text-sm mt-1" />
                    </div>
                    <div class="mb-4">
                        <label for="secretKey" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["SecretKeyLabel"]</label>
                        <InputText id="secretKey" @bind-Value="NewTotpCode.SecretKey" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" placeholder="@Localizer["SecretKeyPlaceholder"]" />
                        <ValidationMessage For="@(() => NewTotpCode.SecretKey)" class="text-red-600 dark:text-red-400 text-sm mt-1" />
                    </div>
                    <div class="flex justify-end">
                        <button id="save-totp-code" type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
                            @Localizer["SaveButton"]
                        </button>
                    </div>
                </EditForm>
            </div>
        }

        <div class="grid grid-cols-1 gap-4 mt-4">
            @foreach (var totpCode in TotpCodeList.Where(t => !t.IsDeleted))
            {
                <div class="p-2 ps-3 pe-3 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600">
                    <div class="flex justify-between items-center gap-2">
                        <div class="flex items-center flex-1">
                            <h4 class="text-sm font-medium text-gray-900 dark:text-white">@totpCode.Name</h4>
                        </div>
                        <div class="flex items-center gap-2">
                            <div class="flex flex-col items-end">
                                <div class="text-sm text-gray-500 dark:text-gray-400">@Localizer["SaveToViewCodeMessage"]</div>
                            </div>
                            <button type="button" @onclick="() => DeleteTotpCode(totpCode)" class="delete-totp-code text-red-600 hover:text-red-800 dark:text-red-500 dark:hover:text-red-400">
                                <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                                    <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"></path>
                                </svg>
                            </button>
                        </div>
                    </div>
                </div>
            }
        </div>
    }
</div>

@code {
    private IStringLocalizer Localizer => LocalizerFactory.Create("Components.Main.Components.TotpCodes.TotpCodes", "AliasVault.Client");

    private IStringLocalizer SharedLocalizer => LocalizerFactory.Create("SharedResources", "AliasVault.Client");

    /// <summary>
    /// The list of TOTP codes.
    /// </summary>
    [Parameter]
    public List<TotpCode> TotpCodeList { get; set; } = [];

    /// <summary>
    /// Event callback for when the TOTP codes list changes.
    /// </summary>
    [Parameter]
    public EventCallback<List<TotpCode>> TotpCodesChanged { get; set; }

    private bool IsAddFormVisible { get; set; } = false;
    private TotpCodeEdit NewTotpCode { get; set; } = new();
    private List<Guid> OriginalTotpCodeIds { get; set; } = [];

    /// <inheritdoc/>
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
    }

    /// <inheritdoc/>
    protected override void OnInitialized()
    {
        base.OnInitialized();

        // Keep track of the original TOTP codes.
        OriginalTotpCodeIds = TotpCodeList.Where(t => !t.IsDeleted).Select(t => t.Id).ToList();
    }

    /// <summary>
    /// Shows the add form.
    /// </summary>
    private void ShowAddForm()
    {
        NewTotpCode = new TotpCodeEdit();
        IsAddFormVisible = true;
    }

    /// <summary>
    /// Hides the add form.
    /// </summary>
    private void HideAddForm()
    {
        IsAddFormVisible = false;
    }

    /// <summary>
    /// Adds a new TOTP code called from the add form.
    /// </summary>
    private async Task AddTotpCode()
    {
        // Sanitize the secret key by converting from URI to secret key and name.
        try {
            var (secretKey, name) = TotpHelper.SanitizeSecretKey(NewTotpCode.SecretKey, NewTotpCode.Name);
            NewTotpCode.SecretKey = secretKey;
            NewTotpCode.Name = name;
        }
        catch (Exception ex)
        {
            GlobalNotificationService.AddErrorMessage(ex.Message, true);
            return;
        }

        // Create a new TOTP code in memory
        var newTotpCode = NewTotpCode.ToEntity();
        newTotpCode.Name = NewTotpCode.Name ?? "Authenticator";

        // Add to the list
        TotpCodeList.Add(newTotpCode);

        // Notify parent component
        await TotpCodesChanged.InvokeAsync(TotpCodeList);

        HideAddForm();
        StateHasChanged();
    }

    /// <summary>
    /// Deletes a TOTP code.
    /// </summary>
    /// <param name="totpCode">The TOTP code to delete.</param>
    private async Task DeleteTotpCode(TotpCode totpCode)
    {
        // Show confirmation modal.
        var result = await ConfirmModalService.ShowConfirmation(Localizer["DeleteTotpCodeTitle"], Localizer["DeleteTotpCodeConfirmation"], SharedLocalizer["Confirm"], SharedLocalizer["Cancel"]);
        if (!result)
        {
            return;
        }

        // Check if the TOTP code was part of the original set
        if (OriginalTotpCodeIds.Contains(totpCode.Id))
        {
            // If it was part of the original set, we soft delete it
            var totpCodeToDelete = TotpCodeList.FirstOrDefault(t => t.Id == totpCode.Id);
            if (totpCodeToDelete is not null)
            {
                totpCodeToDelete.IsDeleted = true;
                totpCodeToDelete.UpdatedAt = DateTime.UtcNow;
            }
        }
        else
        {
            // If it was not part of the original set, we hard delete it
            TotpCodeList.Remove(totpCode);
        }

        // Notify parent component
        await TotpCodesChanged.InvokeAsync(TotpCodeList);
        StateHasChanged();
    }
}
